﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Network.Interface;
using Network.Interface.Packets;

namespace Network.Server.ClientHandling
{
    public abstract class ClientHandler<TSp, TCp>
        where TSp : ServerPacket
        where TCp : ClientPacket
    {
        #region FIELDS

        protected readonly UdpClient Server;
        protected readonly IPEndPoint EndPoint;
        private bool mDropClient;

        protected readonly Queue<byte[]> WaitList;
        protected readonly Thread ClientThread;

        #endregion

        public long LastUpdate { get; private set; }

        public bool IsActive { get; private set; }

        public Action<string> Log { get; set; }

        public virtual void DropClient()
        {
            mDropClient = true;
        }

        protected ClientHandler(UdpClient server, IPEndPoint endPoint)
        {
            Server = server;
            EndPoint = endPoint;
            LastUpdate = DateTime.Now.Ticks;
            IsActive = true;
            mDropClient = false;
            WaitList = new Queue<byte[]>();
            ClientThread = new Thread(HandleClientActions);
            ClientThread.Start();
        }

        protected abstract TSp UpdateClient(TCp packet);

        public virtual void AddTask(byte[] task)
        {
            WaitList.Enqueue(task);
            LastUpdate = DateTime.Now.Ticks;
        }

        protected virtual void HandleClientActions()
        {
            while (!mDropClient)
            {
                try
                {
                    if (WaitList.Count > 0)
                    {
                        byte[] bytes = WaitList.Dequeue();
                        TCp packet = SerializationHelper.ByteArrayToObject<TCp>(bytes);
                        byte[] objectToByteArray = SerializationHelper.ObjectToByteArray(UpdateClient(packet));
                        Server.Send(objectToByteArray, objectToByteArray.Length, EndPoint);
                        LastUpdate = DateTime.Now.Ticks;
                    }
                    else
                    {
                        if (new TimeSpan(DateTime.Now.Ticks - LastUpdate).TotalSeconds > ServerConfigurator.Instance.ClientTimeout)
                        {
                            AddToLog(string.Format("Client from {0} disconnected.", EndPoint));
                            break;
                        }

                        Thread.Sleep(ServerConfigurator.Instance.ClientUpdateTime);
                    }
                }
                catch (SocketException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    AddToLog(string.Format("Exeption occured: \n{0}", ex.Message));
                    break;
                }
            }
            
            IsActive = false;
        }

        protected void AddToLog(string text)
        {
            if(Log != null)
                Log(text);
        }
    }
}
